module IPFS.Client.Command.Misc (
    id,
    IdInfo(..),
    version,
    Version(..),
    dns,
    DNSInfo(..),
    ping,
    PingInfo(..),
    shutdown
) where

import Prelude

import Data.Argonaut (class DecodeJson, Json, decodeJson, fromString)
import Data.Argonaut.Decode ((.:))
import Data.BigInt (BigInt)
import Data.Either (Either)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Integral (toBigInt)
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff)
import Foreign.Object as O
import IPFS.Client as C
import IPFS.Client.Command (Command, defCommand, genQSObject)
import IPFS.Client.Utils.SendRequest (execEffectStreamDecodeJsonCommand, execJsonCommand, execCommand)
import Network.HTTP as HTTP

--------------------------id--------------------------------
data IdInfo = IdInfo {
　id::String,
  publicKey::String,
  agentVersion::String,
  protocolVersion::String,
  addresses::Array String
}

derive instance genericIdInfo :: Generic IdInfo _
instance showIdInfo :: Show IdInfo where
  show s = genericShow s

instance idInfoDecodeJson :: DecodeJson IdInfo where
  decodeJson::Json -> Either String IdInfo
  decodeJson json = do
    djson           <- decodeJson json
    id_             <- djson .: "ID"
    publicKey       <- djson .: "PublicKey"
    agentVersion    <- djson .: "AgentVersion"
    protocolVersion <- djson .: "ProtocolVersion"
    addresses       <- djson .: "Addresses"
    pure $ IdInfo {id:id_,publicKey:publicKey,agentVersion:agentVersion,protocolVersion:protocolVersion,addresses:addresses }


idCommand::Command
idCommand = defCommand {path = "id",args = Nothing,qs = O.empty}

id::C.Client -> Aff (Either String IdInfo)
id client = execJsonCommand client idCommand

--------------------------version--------------------------------
data Version = Version {
    version::String,
    commit ::String,
    repo   ::String,
    system ::String,
    golang ::String
}

derive instance genericVersion :: Generic Version _
instance showVersion :: Show Version where
  show s = genericShow s

instance versionDecodeJson :: DecodeJson Version where
  decodeJson::Json -> Either String Version
  decodeJson json = do
    djson        <- decodeJson json
    version_     <- djson .: "Version"
    commit       <- djson .: "Commit"
    repo         <- djson .: "Repo"
    system       <- djson .: "System"
    golang       <- djson .: "Golang"
    pure $ Version {version:version_,commit:commit,repo:repo,system:system,golang:golang}

versionCommand::Command
versionCommand = defCommand {path = "version",args = Nothing,qs = O.empty}

version::C.Client -> Aff (Either String Version)
version client = execJsonCommand client versionCommand

--------------------------dns--------------------------------
data DNSInfo = DNSInfo {
    path::String
}
derive instance genericDNSInfo :: Generic DNSInfo _
instance showDNSInfo :: Show DNSInfo where
  show s = genericShow s

instance dNSInfoDecodeJson :: DecodeJson DNSInfo where
  decodeJson::Json -> Either String DNSInfo
  decodeJson json = do
    djson  <- decodeJson json
    path   <- djson .: "Path"
    pure $ DNSInfo {path:path}

dnsCommand::String -> Maybe Boolean -> Command
dnsCommand domain recursive = defCommand {path = "dns",args = Just [fromString domain],qs = genQSObject ["recursive" /\ (show <$> recursive)] O.empty}

dns::C.Client -> String -> Maybe Boolean -> Aff (Either String DNSInfo)
dns client domain recursive = execJsonCommand client $ dnsCommand domain recursive

--------------------------ping--------------------------------
data PingInfo = PingInfo {
    success::Boolean,
    time::BigInt,
    text::String
}

derive instance genericPingInfo :: Generic PingInfo _
instance showPingInfo :: Show PingInfo where
  show s = genericShow s

instance pingInfoDecodeJson :: DecodeJson PingInfo where
  decodeJson::Json -> Either String PingInfo
  decodeJson json = do
    djson  <- decodeJson json
    success      <- djson .: "Success"
    time::Number <- djson .: "Time"
    text         <- djson .: "Text"
    pure $ PingInfo {success:success,time:toBigInt time,text:text}

pingCommand::String -> Maybe Int -> Command
pingCommand peerId count = defCommand {path = "ping",args = Just [fromString peerId],qs = genQSObject ["count" /\ (show <$> count)] O.empty}

ping::C.Client -> String -> Maybe Int -> (HTTP.Response -> Effect Unit) -> (Either String PingInfo -> Effect Unit)　-> Effect Unit
ping client peerId count startCallback  callback = execEffectStreamDecodeJsonCommand client (pingCommand peerId count) startCallback callback *> pure unit

--------------------------shutdown--------------------------------
shutdownCommand::Command
shutdownCommand = defCommand {path = "shutdown",args = Nothing,qs = O.empty}

shutdown::C.Client -> Aff (Either String Unit)
shutdown client  = do 
  e <- execCommand client shutdownCommand
  pure $ (const unit ) <$> e
